
#include <BSystem>
#include <BOpenGL>
#include <BRender>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BRoundHandler>

#include <BContact>
#include <BMaterial>
#include <BMesh>
#include <BSphere>
#include <BCamera>
#include <BViewer>
#include <BScene>

#include "MainHandler.h"

MainHandler::MainHandler()
{
	_dragHandler = new DragHandler();
	_leftPressed = false;
	_spaceShown = true;
	_showContacts = false;
	_showOnlyDynamicBodys = false;
}
MainHandler::~MainHandler()
{

}

void MainHandler::setDragImpulse(BReal impulseLimit)
{
	_dragHandler->pointRestraint()->setImpulseLimit(impulseLimit);
}
BReal MainHandler::dragImpulse() const
{
	return _dragHandler->pointRestraint()->impulseLimit();
}

void MainHandler::showSleeps(bool onoff)
{
	_showSleeps = onoff;
}
void MainHandler::showContacts(bool onoff)
{
	_showContacts = onoff;
}
void MainHandler::showOnlyDynamicBodys(bool onoff)
{
	_showOnlyDynamicBodys = onoff;
}

void MainHandler::drawPickedFeature(BRender& render, BPickHit* hit)
{
	BNode* node = hit->node();
	if (BMesh* mesh = dynamic_cast<BMesh*>(hit->shape()))
	{
		int index = hit->index();
		BTriangle triangle = mesh->triangle(index);
		render.fillTriangle(triangle);
	}
	else if (BGeometry* geometry = dynamic_cast<BGeometry*>(hit->shape()))
	{
		BVectorArray* vertices = geometry->vertices();
		BElement* element = hit->element();
		if (!element)
			return;
		int index = hit->index();
		if (element->primitive() == Primitive_Polygon)
		{
			if (BIntArray* indices = element->indices())
			{
				render.fillPolygon(*vertices, *indices);
			}
			else if (BPieceArray* pieces = element->pieces())
			{

			}
			else
			{
				int first = element->first();
				int count = element->count();
				render.fillPolygon(vertices->data() + first, count);
			}
		}
		else if (element->primitive() == Primitive_Triangles)
		{
			if (BIntArray* indices = element->indices())
			{
				int ia = indices->at(index);
				int ib = indices->at(index + 1);
				int ic = indices->at(index + 2);
				const BVector& va = vertices->at(ia);
				const BVector& vb = vertices->at(ib);
				const BVector& vc = vertices->at(ic);
				render.fillTriangle(va, vb, vc);
			}
			else if (BPieceArray* pieces = element->pieces())
			{
				index *= 3;
				int ia = pieces->at(index).v();
				int ib = pieces->at(index + 1).v();
				int ic = pieces->at(index + 2).v();
				const BVector& va = vertices->at(ia);
				const BVector& vb = vertices->at(ib);
				const BVector& vc = vertices->at(ic);
				render.fillTriangle(va, vb, vc);
			}
			else
			{
				int i = element->first() + index * 3;
				const BVector& va = vertices->at(i);
				const BVector& vb = vertices->at(i + 1);
				const BVector& vc = vertices->at(i + 2);
				render.fillTriangle(va, vb, vc);
			}
		}
		else if (element->primitive() == Primitive_Quads)
		{
			if (BIntArray* indices = element->indices())
			{
				int ia = indices->at(index);
				int ib = indices->at(index + 1);
				int ic = indices->at(index + 2);
				int id = indices->at(index + 3);
				const BVector& va = vertices->at(ia);
				const BVector& vb = vertices->at(ib);
				const BVector& vc = vertices->at(ic);
				const BVector& vd = vertices->at(id);
				render.fillQuad(va, vb, vc, vd);
			}
			else if (BPieceArray* pieces = element->pieces())
			{
				index *= 3;
				int ia = pieces->at(index).v();
				int ib = pieces->at(index + 1).v();
				int ic = pieces->at(index + 2).v();
				int id = pieces->at(index + 3).v();
				const BVector& va = vertices->at(ia);
				const BVector& vb = vertices->at(ib);
				const BVector& vc = vertices->at(ic);
				const BVector& vd = vertices->at(id);
				render.fillQuad(va, vb, vc, vd);
			}
			else
			{
				int i = element->first() + index * 3;
				const BVector& va = vertices->at(i);
				const BVector& vb = vertices->at(i + 1);
				const BVector& vc = vertices->at(i + 2);
				const BVector& vd = vertices->at(i + 3);
				render.fillQuad(va, vb, vc, vd);
			}
		}
	}
}

void MainHandler::attached(BViewer* viewer)
{
	
}
void MainHandler::detached(BViewer* viewer)
{
	
}

void MainHandler::render(BRender& render)
{
	if (1)
	{
		render.setLighting(false);
		render.setDepthOffset(-1);
		BReal width = 200;
		BReal height = 200;
		render.setLineWidth(2);
		render.setColor(200, 0, 0);
		render.drawLine(BVector(0, 0, 0), BVector(width, 0, 0));
		render.setColor(0, 200, 0);
		render.drawLine(BVector(0, 0, 0), BVector(0, height, 0));
		render.setColor(0, 0, 200);
		render.drawLine(BVector(0, 0, 0), BVector(0, 0, bMax(width, height)));
		render.setDepthOffset(0);
		render.setLighting(true);
	}

	if (_hoveredNode)
	{
		render.setLighting(false);
		if (_hoveredShape)
		{
			BBox box = _hoveredShape->box() * _hoveredShape->matrix() * _hoveredNode->matrix();
			render.setLineWidth(1);
			render.setColor(100, 255, 100);
			render.drawBox(box);
			if (_hoveredHit)
			{
				render.pushMatrix(_hoveredShape->matrix() * _hoveredNode->matrix());
				render.setLighting(false);
				render.setDepthTest(false);
				render.setColor(200, 100, 0);
				drawPickedFeature(render, _hoveredHit);
				render.popMatrix();
			}
		}
	}
	else
	{
		if (_hoveredShape)
		{
			BBox box = _hoveredShape->box() * _hoveredShape->matrix();
			render.setLighting(false);
			render.setLineWidth(1);
			render.setColor(100, 255, 100);
			render.drawBox(box);
		}
	}
	if (_selectedNode)
	{
		render.setLighting(false);
		render.setLineSmooth(true);
		render.setColor(255, 255, 255);

		render.setLineWidth(1);
		render.setLineStipple(0xf0f0);
		render.setDepthTest(true);
		render.drawSpace(_selectedNode->space());
		render.setLineStipple(0);

		render.setLineWidth(2);
		render.setDepthTest(true);
		render.setColor(255, 255, 255);
		//render.drawSpace(node->space());
		render.drawBox(_selectedNode->box() * _selectedNode->matrix());
		for (int s = 0; s < _selectedNode->shapeCount(); s++)
		{
			BShape* shape = _selectedNode->shape(s);
			BBox box = shape->box() * shape->matrix() * _selectedNode->matrix();
			render.setColor(100, 255, 100);
			render.drawBox(box);
		}
	}
	if (_showSleeps)
	{
		render.setLighting(false);
		render.setLineSmooth(true);
		render.setDepthTest(true);
		render.setLineStipple(0xf0f0);
		for (int i = 0; i < this->scene()->bodyCount(); i++)
		{
			BBody* body = this->scene()->body(i);
			if (body->state() == BBody::State_Sleeping)
			{
				render.setColor(0, 0, 250);
				render.drawBox(body->box() * body->matrix());
			}
		}
		render.setLineStipple(0);
	}
	if (_showContacts)
	{
		BValue queryValue;
		this->scene()->query("contacts", queryValue);
		render.setDepthTest(false);
		render.setLineWidth(2);
		BContactArray& contacts = queryValue;
		for (int i = 0; i < contacts.size(); i++)
		{
			BContact* contact = contacts[i];
			BBody* bodyA = contact->bodyA;
			BBody* bodyB = contact->bodyB;
			BVector positionA = bodyA->matrix().position();
			BVector positionB = bodyB->matrix().position();

			if (bodyA->mass() > REAL_EPSILON)
			{
				render.setColor(200, 0, 0);
				render.drawLine(positionA, contact->pointA);
			}

			if (bodyB->mass() > REAL_EPSILON)
			{
				render.setColor(0, 0, 200);
				render.drawLine(positionB, positionB + contact->pointB);
			}

			if (_showOnlyDynamicBodys && (bodyA->mass() < REAL_EPSILON || bodyB->mass() < REAL_EPSILON))
				continue;
			
			render.setColor(200, 0, 0);
			render.drawLine(contact->pointA, contact->pointA + contact->normalA);
			
			render.setColor(0, 0, 200);
			render.drawLine(contact->pointB, contact->pointB + contact->normalB);

		}
	}
}

void MainHandler::updateEvent(const BEvent& event)
{
	BCamera* camera = this->camera();
	if (camera && _transStep > 0)
	{
		BVector position = camera->position();
		BReal radius = _transSpace.radius() * 2;
		BReal distance = (position - _transSpace.center()).length() - radius;
		distance /= _transStep;
		BVector center = camera->center();
		BVector dest = _transSpace.center();
		camera->setCenter(center + (dest - center) / _transStep);
		position += camera->direct() * distance;
		camera->setPosition(position);
		_transStep--;
		if (_transStep == 0)
		{
			camera->setCenter(dest);
			this->emit("camera-focus-finished", camera);
		}
	}
}
void MainHandler::freshEvent(const BEvent& event)
{

}

void MainHandler::keyPressEvent(const BKeyEvent& event)
{
	if (event.key() == Key_Space)
	{
		if (_selectedNode)
		{
			_transSpace = _selectedNode->space();
		}
		else
		{
			_transSpace = this->scene()->space();
		}
		_transStep = 10;
		event.accept();
	}
	if (event.key() == Key_Alt)
	{
		this->addChild(_dragHandler);
		event.accept();
	}
	if (event.key() == Key_Delete && _selectedNode)
	{
		BScene* scene = this->viewer()->scene();
	}
}
void MainHandler::keyReleaseEvent(const BKeyEvent& event)
{
	if (event.key() == Key_Alt)
	{
		this->removeChild(_dragHandler);
		event.accept();
	}
}

void MainHandler::mouseEnterEvent(const BMouseEvent& event)
{
	BRoundHandler::mouseEnterEvent(event);
}
void MainHandler::mouseLeaveEvent(const BMouseEvent& event)
{
	BRoundHandler::mouseLeaveEvent(event);
}
void MainHandler::mousePressEvent(const BMouseEvent& event)
{
	if (event.button() == Button_Left)
	{
		const BPoint& pos = event.pos();
		BLine line = this->viewer()->line(pos);
		_leftPressed = true;
		_linePressed = line;
		if (_selectedNode != _hoveredNode)
		{
			_selectedNode = _hoveredNode;
			this->fresh();
		}
	}
	BRoundHandler::mousePressEvent(event);
}
void MainHandler::mouseReleaseEvent(const BMouseEvent& event)
{
	if (_leftPressed)
	{
		const BPoint& pos = event.pos();
		BLine line = this->viewer()->line(pos);
		_leftPressed = false;
	}
	BRoundHandler::mouseReleaseEvent(event);
}
void MainHandler::mouseMoveEvent(const BMouseEvent& event)
{
	const BPoint& pos = event.pos();
	BLine line = this->viewer()->line(pos);
	if (event.button() == Button_None)
	{
		const BPoint& pos = event.pos();
		BLine line = this->viewer()->line(pos);
		BPicker picker(line);
		BNode* hoveredNode = 0;
		BShape* hoveredShape = 0;
		_hoveredHit = this->viewer()->pick(pos);
		if (_hoveredHit)
		{
			_hoveredPoint = _hoveredHit->absolutePoint();
			hoveredNode = _hoveredHit->node();
			hoveredShape = _hoveredHit->shape();
		}
		if (_hoveredNode != hoveredNode || _hoveredShape != hoveredShape)
		{
			_hoveredNode = hoveredNode;
			_hoveredShape = hoveredShape;
			this->fresh();
		}
	}
	BRoundHandler::mouseMoveEvent(event);
}

